Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: PG::SyntaxError (MAYBE-MARKETING-3C) #254

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

revise-dev[bot]
Copy link
Contributor

@revise-dev revise-dev bot commented Feb 13, 2025

The error occurs in the Stock model's search scope where the PostgreSQL tsquery syntax is being generated incorrectly. The original code was attempting to sanitize search terms by removing special characters and adding :* for prefix matching, but it wasn't properly handling empty terms that could result from the sanitization process.

The original implementation:

sanitized_query = query.split.map { |term| "#{term.gsub(/[()&|!:*]/, '')}:*" }.join(" & ")

could produce invalid tsquery strings like "ANF:* & Abercrombie:* & :* & Fitch:* & Co.:" where empty terms resulted in standalone ":" expressions, which is invalid PostgreSQL tsquery syntax.

The fix modifies the query construction process into distinct steps:

  1. Split the input string into terms using spaces (query.split)
  2. Remove special characters from each term that could interfere with tsquery syntax (gsub(/[()&|!:*]/, ''))
  3. Remove any terms that became empty after sanitization (.reject(&:blank?))
  4. Add the :* suffix to each remaining term for prefix matching
  5. Join the terms with " & " to create a valid tsquery expression

This ensures that we never generate invalid tsquery syntax with empty terms, while maintaining the original functionality of prefix matching and term combination.

The search will now properly handle cases where:

  • Terms contain special characters
  • Multiple consecutive spaces exist between terms
  • Terms become empty after sanitization
  • Complex company names with special characters (like "AT&T" or "P&G")

The change preserves the original intent of the search functionality while making it more robust against edge cases that could cause PostgreSQL syntax errors.

Error Details

Summary:

PG::SyntaxError: ERROR:  syntax error in tsquery: "ANF:* & Abercrombie:* & :* & Fitch:* & Co.:*" (PG::SyntaxError)

Stacktrace:

active_record/connection_adapters/postgresql/database_statements.rb:162
active_record/connection_adapters/postgresql/database_statements.rb:162
active_record/connection_adapters/abstract/database_statements.rb:556
active_record/connection_adapters/abstract_adapter.rb:1011
active_support/concurrency/null_lock.rb:9
active_record/connection_adapters/abstract_adapter.rb:983
active_record/connection_adapters/abstract/database_statements.rb:555
active_support/notifications/instrumenter.rb:58
sentry/rails/tracing.rb:56
active_record/connection_adapters/abstract_adapter.rb:1129
active_record/connection_adapters/abstract/database_statements.rb:554
active_record/connection_adapters/abstract/database_statements.rb:591
active_record/connection_adapters/abstract/database_statements.rb:547
active_record/connection_adapters/abstract/database_statements.rb:693
active_record/connection_adapters/abstract/database_statements.rb:73
active_record/connection_adapters/abstract/query_cache.rb:248
active_record/connection_adapters/abstract/query_cache.rb:286
active_record/connection_adapters/abstract/query_cache.rb:80
active_record/connection_adapters/abstract/query_cache.rb:284
active_support/concurrency/null_lock.rb:9
active_record/connection_adapters/abstract/query_cache.rb:283
active_record/connection_adapters/abstract/query_cache.rb:248
active_record/relation/calculations.rb:493
active_record/connection_adapters/abstract/connection_pool.rb:418
active_record/connection_handling.rb:310
active_record/relation/calculations.rb:492
active_record/relation.rb:1470
active_record/relation/calculations.rb:491
active_record/relation/calculations.rb:445
active_record/relation/calculations.rb:245
active_record/relation/calculations.rb:102
pagy/backend.rb:23
pagy/backend.rb:13
app/controllers/stocks_controller.rb:39
action_controller/metal/basic_implicit_render.rb:8
abstract_controller/base.rb:226
action_controller/metal/rendering.rb:193
abstract_controller/callbacks.rb:261
active_support/callbacks.rb:120
turbo-rails.rb:24
/home/deploy/maybe-marketing-prod/shared/bundle/ruby/3.4.0/gems/turbo-rails-2.0.11/app/controllers/concerns/turbo/request_id_tracking.rb:10
active_support/callbacks.rb:129
action_text/rendering.rb:25
action_text/engine.rb:71
active_support/callbacks.rb:129
active_support/callbacks.rb:129
sentry/rails/controller_transaction.rb:21
sentry/hub.rb:115
sentry/span.rb:237
sentry/hub.rb:113
sentry-ruby.rb:507
sentry/rails/controller_transaction.rb:18
active_support/callbacks.rb:129
active_support/callbacks.rb:140
abstract_controller/callbacks.rb:260
action_controller/metal/rescue.rb:27
action_controller/metal/instrumentation.rb:76
active_support/notifications.rb:210
active_support/notifications/instrumenter.rb:58
sentry/rails/tracing.rb:56
active_support/notifications.rb:210
action_controller/metal/instrumentation.rb:75
action_controller/metal/params_wrapper.rb:259
active_record/railties/controller_runtime.rb:39
abstract_controller/base.rb:163
action_view/rendering.rb:40
action_controller/metal.rb:252
action_controller/metal.rb:335
action_dispatch/routing/route_set.rb:67
action_dispatch/routing/route_set.rb:50
action_dispatch/journey/router.rb:53
action_dispatch/journey/router.rb:133
action_dispatch/journey/router.rb:126
action_dispatch/journey/router.rb:126
action_dispatch/journey/router.rb:34
action_dispatch/routing/route_set.rb:908
logtail-rails/error_event.rb:24
logtail-rack/http_events.rb:213
logtail-rack/user_context.rb:74
logtail-rails/session_context.rb:16
logtail-rack/http_context.rb:23
logtail/current_context.rb:120
logtail/current_context.rb:44
logtail-rack/http_context.rb:22
rack/static.rb:161
rack/tempfile_reaper.rb:20
rack/etag.rb:29
rack/conditional_get.rb:31
rack/head.rb:15
action_dispatch/http/permissions_policy.rb:38
action_dispatch/http/content_security_policy.rb:38
rack/session/abstract/id.rb:274
rack/session/abstract/id.rb:268
action_dispatch/middleware/cookies.rb:706
action_dispatch/middleware/callbacks.rb:31
active_support/callbacks.rb:100
action_dispatch/middleware/callbacks.rb:30
sentry/rails/rescued_exception_interceptor.rb:14
action_dispatch/middleware/debug_exceptions.rb:31
sentry/rack/capture_exceptions.rb:30
sentry/hub.rb:269
sentry-ruby.rb:420
sentry/rack/capture_exceptions.rb:21
sentry/hub.rb:59
sentry-ruby.rb:400
sentry/rack/capture_exceptions.rb:20
action_dispatch/middleware/show_exceptions.rb:32
rails/rack/logger.rb:41
rails/rack/logger.rb:29
action_dispatch/middleware/remote_ip.rb:96
action_dispatch/middleware/request_id.rb:34
rack/method_override.rb:28
rack/runtime.rb:24
active_support/cache/strategy/local_cache_middleware.rb:29
action_dispatch/middleware/executor.rb:16
action_dispatch/middleware/static.rb:27
rack/sendfile.rb:114
action_dispatch/middleware/ssl.rb:92
rails/engine.rb:535
puma/configuration.rb:279
puma/request.rb:99
puma/thread_pool.rb:390
puma/request.rb:98
puma/server.rb:472
puma/server.rb:254
puma/thread_pool.rb:167

Tip

You can make revisions or ask questions of Revise.dev by using /revise in any comment or review!

  • /revise Add a comment above the method to explain why we're making this change.
  • /revise Why did you choose to make this change specifically?

Important

If something doesn’t look right, click to retry this interaction.

Quick links: View in SentryView in Revise

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0 participants